#!/bin/python
from pyprotobuf.compiler import Compiler, CompilerInput, StringCompilerInput
import argparse
import collections
import logging
import os
import pkg_resources
import pyprotobuf
import sys


ENTRYPOINT = 'pyprotobuf.generators'


GENERATORS = collections.OrderedDict([
    ('closure', 'pyprotobuf.generators.closurelib'),
    ('externs', 'pyprotobuf.generators.externs'),
    ('go', 'pyprotobuf.generators.go'),
    ('protorpc', 'pyprotobuf.generators.protorpc'),
])


def import_generator(module_name):
    try:
        module = __import__(module_name, globals(), locals(), ['__generator__'], 0)
        return module.__generator__
    except ImportError:
        return None


def main(args=None):
    """
        generate format [inputs...] [options]
    """

    generators = {}

    # include default generators
    for name, module in GENERATORS.items():
        generators[name] = import_generator(module)

    # search for entrypoints defined in setup.py or extended with plugins
    # generator modules are defined in [pyprotobuf.generators]
    # generator module must have a __generator__ symbol pointing to a generator class
    for entrypoint in pkg_resources.iter_entry_points(ENTRYPOINT):
         module = entrypoint.load()
         generators[entrypoint.name] = getattr(module, '__generator__', None)

    generator_names = generators.keys()

    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('input', default=[], nargs="+")
    parser.add_argument('--path', '-p', default=[], help="Include search paths. Can be specified multiple times.", action='append')
    parser.add_argument('--generator', default=None, help="Specify the path to a generator module.")
    parser.add_argument('--format', default=None, choices=generator_names, help="Specify the output format.")
    parser.add_argument('--debug', '-d', default=False, action='store_true')
    parser.add_argument('--help', '-h', default=False, action='store_true', help='Show this help message. Specify --format or --generator to see generator specific help')

    args = parser.parse_known_args()[0]

    level = logging.INFO


    if getattr(args, 'debug'):
        level = logging.DEBUG

    logging.basicConfig(level=level)


    overrride_generator = getattr(args, 'generator', None)

    format = args.format

    if overrride_generator is not None:
        overrride_generator = os.path.abspath(overrride_generator)

        if sys.version_info[0] >= 3:
            import importlib.machinery
            loader = importlib.machinery.SourceFileLoader("pyprotobuf.generators.user", overrride_generator)
            module = loader.load_module("pyprotobuf.generators.user")
            generator_class = getattr(module, '__generator__')
        else:
            import imp
            module = imp.load_source('pyprotobuf.generators.user', overrride_generator)
            generator_class = getattr(module, '__generator__')

        if generator_class is None:
            parser.exit(-1, "Invalid generator: %s" % args.format)

        generators['user'] = generator_class
        format = 'user'


    generator_class = generators.get(format)
    generator_parsers = {}
    for k, generator in generators.items():
        generator_parser = generator_parsers[k] = argparse.ArgumentParser(parents=[parser], add_help=False)
        generator.add_arguments(generator_parser)


    # select the generator for help
    if format in generators:
        parser = generator_parsers[format]


    args = parser.parse_args()


    if args.help:
        parser.print_help()
        parser.exit()

    if generator_class is None:
        parser.error("Invalid generator: %s" % args.format)

    inputs = []
    for path in args.input:
        path = os.path.abspath(path)
        inputs.append(CompilerInput(path))

    compiler = Compiler()
    compiler.set_inputs(inputs)
    compiler.import_paths += map(os.path.abspath, args.path)

    try:
        root = compiler.compile()

        # generate the ast of the input filenodes
        generator = generator_class()
        generator.set_arguments(args)

        for input in inputs:
            output = generator.generate_file(input.ast, header=True)
            print (output.decode('utf-8'))
    except (pyprotobuf.parser.ParseError, pyprotobuf.framevisitor.ResolveError) as e:
        logging.exception(e)
        parser.exit(-1)




def parse_and_generate(string, path, generator_class, arguments=None):
    input = StringCompilerInput(path, string)
    compiler = Compiler()
    compiler.set_inputs([input])

    root = compiler.compile()

    generator = generator_class()
    generator.set_generate_headers(False)
    generator.set_arguments(arguments)

    # TODO: we should probably pass the rootnode too
    output = generator.generate_file(input.ast)

    return output


def _main():
    main()

if __name__ == '__main__':
    _main()
